#include "wxsitemresdata.h"
#include "wxsparent.h"
#include "wxsitemfactory.h"
#include "wxsitemeditor.h"
#include "wxstool.h"
#include "wxsflags.h"
#include "../wxscoder.h"

#include <algorithm>
#include <globals.h>
#include <logmanager.h>
#include <wx/clipbrd.h>
#include <tinywxuni.h>

#if defined(__WXMSW__) && defined(LoadImage)
#undef LoadImage
#endif


using namespace wxsFlags;

namespace {
  const int ToolsTreeImageId = wxsResourceTree::LoadImage( _T( "/images/misc.png" ) );
}

wxsItemResData::wxsItemResData( const wxString& WxsFileName, const wxString& SrcFileName, const wxString& HdrFileName,
                                const wxString& XrcFileName, const wxString& ClassName, const wxString& ClassType,
                                wxsCodingLang Language, bool UseForwardDeclarations, bool WithTranslation,
                                wxsResourceItemId TreeId, wxsItemEditor* Editor, wxsItemResFunctions* Functions ):
  m_WxsFileName( WxsFileName ), m_SrcFileName( SrcFileName ), m_HdrFileName( HdrFileName ),
  m_XrcFileName( XrcFileName ), m_Translation( WithTranslation ), m_ClassName( ClassName ),
  m_ClassType( ClassType ), m_Language( Language ), m_TreeId( TreeId ),
  m_ToolsId(), m_ToolsNodeIsExpanded( false ), M_Code_Editor( Editor ),
  m_Functions( Functions ), m_RootItem( 0 ), m_RootSelection( 0 ),
  m_Preview( 0 ), m_Corrector( this ), m_IsOK( false ),
  m_LockCount( 0 ), m_ReadOnly( false ) {
  if( WxsFileName.empty() && SrcFileName.empty() && HdrFileName.empty() && !XrcFileName.empty() ) {
    m_PropertiesFilter = flFile;
  } else if( !WxsFileName.empty() && !SrcFileName.empty() && !HdrFileName.empty() ) {
    if( XrcFileName.empty() ) {
      m_PropertiesFilter = flSource;
    } else {
      m_PropertiesFilter = flMixed;
    }
  } else {
    m_PropertiesFilter = 0;
  }
  DetectAutoCodeBlocks();
  if( UseForwardDeclarations ) {
    m_PropertiesFilter |= flFwdDeclar;
  }
  Load();
  if( !m_RootItem ) {
    RecreateRootItem();
    m_IsOK = false;
  }
}

wxsItemResData::~wxsItemResData() {
  HidePreview();
  if( GetModified() ) {
    SilentLoad();
    RebuildFiles();
  }
  delete m_RootItem;
  m_RootItem = 0;
  m_RootSelection = 0;
  for( int i = 0; i < GetToolsCount(); i++ ) {
    delete m_Tools[i];
  }
  m_Tools.clear();
  m_PropertiesFilter = 0;
  if( M_Code_Editor && wxsResourceTree::Get() ) {
    wxsResourceItemId ParentId = wxsResourceTree::Get()->GetItemParent( m_TreeId );
    wxsResourceTree::Get()->SelectItem( ParentId );
    wxsResourceTree::Get()->DeleteChildren( m_TreeId );
  }
}

bool wxsItemResData::Load() {
  bool Ret = SilentLoad();
  if( !m_RootItem ) {
    RecreateRootItem();
    if( !m_RootItem ) {
      return false;
    }
  }
  if( !m_Corrector.GlobalCheck() ) {
  }
  StoreUndo();
  m_Undo.Saved();
  if( M_Code_Editor ) {
    M_Code_Editor->UpdateModified();
  }
  if( Ret && !( m_PropertiesFilter & flFile ) ) {
    RebuildSourceCode();
  }
  RebuildTree();
  if( M_Code_Editor ) {
    M_Code_Editor->RebuildPreview();
  }
  SelectItem( m_RootItem, true );
  return Ret;
}

bool wxsItemResData::SilentLoad() {
  switch( m_PropertiesFilter & ( flFile | flMixed | flSource ) ) {
    case flFile:
      m_IsOK = LoadInFileMode();
      break;
    case flMixed:
      m_IsOK = LoadInMixedMode();
      break;
    case flSource:
      m_IsOK = LoadInSourceMode();
      break;
    default:
      m_IsOK = false;
  }
  return m_IsOK;
}

bool wxsItemResData::LoadInFileMode() {
  TiXmlDocument Doc;
  if( !TinyXML::LoadDocument( m_XrcFileName, &Doc ) ) {
    return false;
  }
  TiXmlElement* Resource = Doc.FirstChildElement( "resource" );
  if( !Resource ) {
    return false;
  }
  TiXmlElement* Object = Resource->FirstChildElement( "object" );
  while( Object ) {
    if( cbC2U( Object->Attribute( "name" ) ) == m_ClassName ) {
      break;
    }
    Object = Object->NextSiblingElement( "object" );
  }
  if( !Object ) {
    return false;
  }
  if( cbC2U( Object->Attribute( "class" ) ) != m_ClassType ) {
    return false;
  }
  RecreateRootItem();
  if( !m_RootItem ) {
    return false;
  }
  m_RootItem->XmlRead( Object, true, false );
  LoadToolsReq( Object, true, false );
  return true;
}

bool wxsItemResData::LoadInMixedMode() {
  TiXmlDocument DocExtra;
  if( !TinyXML::LoadDocument( m_WxsFileName, &DocExtra ) ) {
    return false;
  }
  TiXmlDocument DocXrc;
  if( !TinyXML::LoadDocument( m_XrcFileName, &DocXrc ) ) {
    return false;
  }
  TiXmlElement* Resource = DocXrc.FirstChildElement( "resource" );
  if( !Resource ) {
    return false;
  }
  TiXmlElement* Object = Resource->FirstChildElement( "object" );
  while( Object ) {
    if( cbC2U( Object->Attribute( "name" ) ) == m_ClassName ) {
      break;
    }
    Object = Object->NextSiblingElement( "object" );
  }
  if( !Object ) {
    return true;
  }
  if( cbC2U( Object->Attribute( "class" ) ) != m_ClassType ) {
    return false;
  }
  RecreateRootItem();
  if( !m_RootItem ) {
    return false;
  }
  m_RootItem->XmlRead( Object, true, false );
  LoadToolsReq( Object, true, false );
  TiXmlElement* wxSmithNode = DocExtra.FirstChildElement( "wxsmith" );
  if( wxSmithNode ) {
    TiXmlElement* Extra = wxSmithNode->FirstChildElement( "resource_extra" );
    if( Extra ) {
      IdToXmlMapT IdToXmlMap;
      TiXmlElement* _Object = Extra->FirstChildElement( "object" );
      while( _Object ) {
        wxString IdName = cbC2U( _Object->Attribute( "name" ) );
        if( !IdName.empty() ) {
          IdToXmlMap[IdName] = _Object;
        } else if( _Object->Attribute( "root" ) ) {
          IdToXmlMap[_T( "" )] = _Object;
        }
        _Object = _Object->NextSiblingElement( "object" );
      }
      UpdateExtraDataReq( m_RootItem, IdToXmlMap );
      for( int i = 0; i < GetToolsCount(); i++ ) {
        UpdateExtraDataReq( m_Tools[i], IdToXmlMap );
      }
    }
  }
  return true;
}

void wxsItemResData::UpdateExtraDataReq( wxsItem* Item, IdToXmlMapT& Map ) {
  if( Item->GetPropertiesFlags() & flId ) {
    wxString Id = Item->GetIdName();
    if( !Id.empty() ) {
      if( !Item->GetParent() ) {
        Id = _T( "" );
      }
      if( Map.find( Id ) != Map.end() ) {
        TiXmlElement* Object = Map[Id];
        if( cbC2U( Object->Attribute( "class" ) ) == Item->GetClassName() ) {
          Item->XmlRead( Object, false, true );
        }
      }
    }
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      UpdateExtraDataReq( AsParent->GetChild( i ), Map );
    }
  }
}

bool wxsItemResData::LoadInSourceMode() {
  TiXmlDocument Doc;
  if( !TinyXML::LoadDocument( m_WxsFileName, &Doc ) ) {
    Manager::Get()->GetLogManager()->DebugLog( F( _T( "wxSmith: Error loading wxs file (Col: %d, Row:%d): " ) + cbC2U( Doc.ErrorDesc() ), Doc.ErrorCol(), Doc.ErrorRow() ) );
    return false;
  }
  TiXmlElement* wxSmithNode = Doc.FirstChildElement( "wxsmith" );
  if( !wxSmithNode ) {
    return false;
  }
  TiXmlElement* Object = wxSmithNode->FirstChildElement( "object" );
  if( !Object ) {
    return false;
  }
  RecreateRootItem();
  if( !m_RootItem ) {
    return false;
  }
  m_RootItem->XmlRead( Object, true, true );
  LoadToolsReq( Object, true, true );
  return true;
}

void wxsItemResData::RecreateRootItem() {
  delete m_RootItem;
  m_RootItem = wxsItemFactory::Build( m_ClassType, this );
}

void wxsItemResData::LoadToolsReq( TiXmlElement* Node, bool IsXRC, bool IsExtra ) {
  for( TiXmlElement* Object = Node->FirstChildElement( "object" ); Object; Object = Object->NextSiblingElement( "object" ) ) {
    wxString Class = cbC2U( Object->Attribute( "class" ) );
    if( Class.IsEmpty() ) {
      continue;
    }
    const wxsItemInfo* Info = wxsItemFactory::GetInfo( Class );
    if( !Info ) {
      continue;
    }
    if( Info->Type != wxsTTool ) {
      LoadToolsReq( Object, IsXRC, IsExtra );
      continue;
    }
    if( !( GetPropertiesFilter()&flSource ) && !Info->AllowInXRC ) {
      continue;
    }
    wxsItem* Item = wxsItemFactory::Build( Class, this );
    if( !Item ) {
      continue;
    }
    wxsTool* Tool = Item->ConvertToTool();
    if( !Tool ) {
      delete Item;
      continue;
    }
    if( InsertNewTool( Tool ) ) {
      Tool->XmlRead( Object, IsXRC, IsExtra );
    }
  }
}

bool wxsItemResData::Save() {
  m_IsOK = true;
  switch( m_PropertiesFilter & ( flFile | flMixed | flSource ) ) {
    case flFile:
      return SaveInFileMode();
    case flMixed:
      return SaveInMixedMode();
    case flSource:
      return SaveInSourceMode();
    default:
      break;
  }
  return false;
}

bool wxsItemResData::SaveInFileMode() {
  if( RebuildXrcFile() ) {
    m_Undo.Saved();
    return true;
  }
  return false;
}

bool wxsItemResData::SaveInMixedMode() {
  if( !RebuildXrcFile() ) {
    return false;
  }
  TiXmlDocument Doc;
  Doc.InsertEndChild( TiXmlDeclaration( "utf-8", "" ) );
  TiXmlElement* wxSmithNode = Doc.InsertEndChild( TiXmlElement( "wxsmith" ) )->ToElement();
  TiXmlElement* Extra = wxSmithNode->InsertEndChild( TiXmlElement( "resource_extra" ) )->ToElement();
  SaveExtraDataReq( m_RootItem, Extra );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    SaveExtraDataReq( m_Tools[i], Extra );
  }
  if( TinyXML::SaveDocument( m_WxsFileName, &Doc ) ) {
    m_Undo.Saved();
    return true;
  }
  return false;
}

void wxsItemResData::SaveExtraDataReq( wxsItem* Item, TiXmlElement* Node ) {
  if( Item->GetPropertiesFlags() && flId ) {
    wxString Id = Item->GetIdName();
    if( !Id.empty() ) {
      TiXmlElement* Object = Node->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
      if( Item != m_RootItem ) {
        Object->SetAttribute( "name", cbU2C( Id ) );
        Object->SetAttribute( "class", cbU2C( Item->GetClassName() ) );
      } else
      { Object->SetAttribute( "root", "1" ); }
      Item->XmlWrite( Object, false, true );
    }
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      SaveExtraDataReq( AsParent->GetChild( i ), Node );
    }
  }
}

bool wxsItemResData::SaveInSourceMode() {
  TiXmlDocument Doc;
  Doc.InsertEndChild( TiXmlDeclaration( "utf-8", "" ) );
  TiXmlElement* wxSmithNode = Doc.InsertEndChild( TiXmlElement( "wxsmith" ) )->ToElement();
  TiXmlElement* Object = wxSmithNode->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
  m_RootItem->XmlWrite( Object, true, true );
  Object->SetAttribute( "name", cbU2C( m_ClassName ) );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    TiXmlElement* ToolElement = Object->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
    m_Tools[i]->XmlWrite( ToolElement, true, true );
  }
  if( TinyXML::SaveDocument( m_WxsFileName, &Doc ) ) {
    m_Undo.Saved();
    return true;
  }
  return false;
}

void wxsItemResData::RebuildFiles() {
  switch( m_PropertiesFilter & ( flSource | flMixed | flSource ) ) {
    case flSource:
      RebuildSourceCode();
      break;
    case flMixed:
      RebuildSourceCode();
      RebuildXrcFile();
      break;
    default:
      break;
  }
}

void wxsItemResData::DetectAutoCodeBlocks() {
  do {
    if( wxsCoder::Get()->GetCode(
          m_SrcFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "InternalHeadersPCH" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          true, false ).IsEmpty() ) {
      break;
    }
    if( wxsCoder::Get()->GetCode(
          m_HdrFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "HeadersPCH" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          true, false ).IsEmpty() ) {
      break;
    }
    m_PropertiesFilter |= flPchFilter;
  } while( false );
  m_IsEventTable = !wxsCoder::Get()->GetCode(
                     m_HdrFileName,
                     wxsCodeMarks::Beg( wxsCPP, _T( "EventTable" ), m_ClassName ),
                     wxsCodeMarks::End( wxsCPP ),
                     true, false ).IsEmpty();
}

void wxsItemResData::RebuildSourceCode() {
  switch( m_Language ) {
    case wxsCPP: {
      wxStopWatch SW;
      wxsCoderContext Context;
      Context.m_Language = m_Language;
      Context.m_Flags = GetPropertiesFilter();
      if( m_RootItem->GetBaseProps()->m_ParentFromArg ) {
        Context.m_WindowParent = _T( "parent" );
      } else
      { Context.m_WindowParent = _T( "0" ); }
      if( m_Translation ) {
        Context.AddHeader( _T( "<wx/intl.h>" ), _T( "" ), hfLocal | hfInPCH );
      }
      Context.AddHeader( _T( "<wx/string.h>" ), _T( "" ), hfLocal | hfInPCH );
      if( m_PropertiesFilter & flMixed ) {
        Context.m_LocalHeadersNonPCH.insert( _T( "<wx/xrc/xmlres.h>" ) );
      }
      m_RootItem->BuildCode( &Context );
      wxsCoder::Get()->AddCode(
        m_HdrFileName,
        wxsCodeMarks::Beg( wxsCPP, _T( "Declarations" ), m_ClassName ),
        wxsCodeMarks::End( wxsCPP ),
        DeclarationsCode( &Context ),
        false );
      wxsCoder::Get()->AddCode(
        m_HdrFileName,
        wxsCodeMarks::Beg( wxsCPP, _T( "Identifiers" ), m_ClassName ),
        wxsCodeMarks::End( wxsCPP ),
        IdentifiersCode( &Context ),
        false );
      wxsCoder::Get()->AddCode(
        m_SrcFileName,
        wxsCodeMarks::Beg( wxsCPP, _T( "Initialize" ), m_ClassName ),
        wxsCodeMarks::End( wxsCPP ),
        InitializeCode( &Context ),
        false );
      wxsCoder::Get()->AddCode(
        m_SrcFileName,
        wxsCodeMarks::Beg( wxsCPP, _T( "IdInit" ), m_ClassName ),
        wxsCodeMarks::End( wxsCPP ),
        IdInitCode( &Context ),
        false );
      if( m_IsEventTable ) {
        wxsCoder::Get()->AddCode(
          m_SrcFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "EventTable" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          _T( "\n" ),
          false );
      }
      if( m_PropertiesFilter & flPchFilter ) {
        wxsCoder::Get()->AddCode(
          m_SrcFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "InternalHeadersPCH" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          InternalHeadersCode( &Context ),
          false );
        wxsCoder::Get()->AddCode(
          m_SrcFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "InternalHeaders" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          InternalHeadersNoPCHCode( &Context ),
          false );
        wxsCoder::Get()->AddCode(
          m_HdrFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "HeadersPCH" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          HeadersCode( &Context ),
          false );
        wxsCoder::Get()->AddCode(
          m_HdrFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "Headers" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          HeadersNoPCHCode( &Context ),
          false );
      } else {
        wxsCoder::Get()->AddCode(
          m_SrcFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "InternalHeaders" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          InternalHeadersAllCode( &Context ),
          false );
        wxsCoder::Get()->AddCode(
          m_HdrFileName,
          wxsCodeMarks::Beg( wxsCPP, _T( "Headers" ), m_ClassName ),
          wxsCodeMarks::End( wxsCPP ),
          HeadersAllCode( &Context ),
          false );
      }
      wxsCoder::Get()->Flush( 500 );
      break;
    }
    case wxsUnknownLanguage:
    default: {
      wxsCodeMarks::Unknown( _T( "wxsItemResData::RebuildSourceCode" ), m_Language );
    }
  }
}


wxString GenerateCodeFromSet( const wxsCoderContext::wxStringSet &set, const wxString &prefix, const wxString &suffix ) {
  std::vector<wxString> array;
  array.reserve( set.size() );
  for( const wxString &item : set ) {
    array.push_back( item );
  }
  std::sort( array.begin(), array.end() );
  wxString Code;
  for( const wxString &item : array ) {
    Code += prefix;
    Code += item;
    Code += suffix;
  }
  return Code;
}

wxString wxsItemResData::DeclarationsCode( wxsCoderContext* Ctx ) {
  wxString Code = _T( "\n" );
  Code += GenerateCodeFromSet( Ctx->m_GlobalDeclarations, wxEmptyString, wxT( "\n" ) );
  return Code;
}

wxString wxsItemResData::IdentifiersCode( wxsCoderContext* Ctx ) {
  wxString Code = _T( "\n" );
  for( size_t Count = Ctx->m_IdEnumerations.Count(), Index = 0; Count > 0; Index++, Count-- ) {
    Code += Ctx->m_IdEnumerations[Index];
    Code += _T( "\n" );
  }
  return Code;
}

wxString wxsItemResData::InitializeCode( wxsCoderContext* Ctx ) {
  wxString Code = _T( "\n" );
  Code += GenerateCodeFromSet( Ctx->m_LocalDeclarations, wxEmptyString, wxT( "\n" ) );
  if( Code.Length() > 1 ) {
    Code += _T( "\n" );
  }
  if( Ctx->m_Flags & flSource ) {
    Code += Ctx->m_BuildingCode;
  } else {
    Code += XRCLoadingCode();
    Code += Ctx->m_XRCFetchingCode;
  }
  if( !Ctx->m_EventsConnectingCode.IsEmpty() ) {
    Code += _T( "\n" );
    Code += Ctx->m_EventsConnectingCode;
  }
  return Code;
}

wxString wxsItemResData::IdInitCode( wxsCoderContext* Ctx ) {
  wxString Code = _T( "\n" );
  for( size_t Count = Ctx->m_IdInitializions.Count(), Index = 0; Count > 0; Index++, Count-- ) {
    Code += Ctx->m_IdInitializions[Index];
    Code += _T( "\n" );
  }
  return Code;
}

wxString wxsItemResData::HeadersCode( wxsCoderContext* Ctx ) {
  wxString Code;
  Code += GenerateCodeFromSet( Ctx->m_GlobalHeaders, wxT( "\n#include " ), wxEmptyString );
  Code += GenerateCodeFromSet( Ctx->m_ForwardDeclarations, wxT( "\nclass " ), wxT( ";" ) );
  return Code + _T( "\n" );
}

wxString wxsItemResData::HeadersNoPCHCode( wxsCoderContext* Ctx ) {
  wxString Code;
  Code += GenerateCodeFromSet( Ctx->m_GlobalHeadersNonPCH, wxT( "\n#include " ), wxEmptyString );
  Code += GenerateCodeFromSet( Ctx->m_ForwardDeclarationsNonPCH, wxT( "\nclass " ), wxT( ";" ) );
  return Code + _T( "\n" );
}

wxString wxsItemResData::HeadersAllCode( wxsCoderContext* Ctx ) {
  wxString Code;
  Code += GenerateCodeFromSet( Ctx->m_GlobalHeaders, wxT( "\n#include " ), wxEmptyString );
  Code += GenerateCodeFromSet( Ctx->m_GlobalHeadersNonPCH, wxT( "\n#include " ), wxEmptyString );
  Code += GenerateCodeFromSet( Ctx->m_ForwardDeclarations, wxT( "\nclass " ), wxT( ";" ) );
  Code += GenerateCodeFromSet( Ctx->m_ForwardDeclarationsNonPCH, wxT( "\nclass " ), wxT( ";" ) );
  return Code + _T( "\n" );
}

wxString wxsItemResData::InternalHeadersCode( wxsCoderContext* Ctx ) {
  wxString Code;
  for( wxsCoderContext::wxStringSet::iterator i = Ctx->m_LocalHeaders.begin(); i != Ctx->m_LocalHeaders.end(); ++i ) {
    Code += _T( "\n#include " );
    Code += *i;
  }
  return Code + _T( "\n" );
}

wxString wxsItemResData::InternalHeadersNoPCHCode( wxsCoderContext* Ctx ) {
  wxString Code;
  Code += GenerateCodeFromSet( Ctx->m_LocalHeadersNonPCH, wxT( "\n#include " ), wxEmptyString );
  return Code + _T( "\n" );
}

wxString wxsItemResData::InternalHeadersAllCode( wxsCoderContext* Ctx ) {
  wxString Code;
  Code += GenerateCodeFromSet( Ctx->m_LocalHeaders, wxT( "\n#include " ), wxEmptyString );
  Code += GenerateCodeFromSet( Ctx->m_LocalHeadersNonPCH, wxT( "\n#include " ), wxEmptyString );
  return Code + _T( "\n" );
}

wxString wxsItemResData::XRCLoadingCode() {
  wxString Parent = _T( "0" );
  if( m_RootItem->GetBaseProps()->m_ParentFromArg ) {
    Parent = _T( "parent" );
  }
  return _T( "wxXmlResource::Get()->LoadObject(this," ) + Parent + _T( "," ) +
         wxsCodeMarks::WxString( wxsCPP, m_ClassName, false ) + _T( "," ) +
         wxsCodeMarks::WxString( wxsCPP, m_ClassType, false ) + _T( ");\n" );
}

bool wxsItemResData::RebuildXrcFile() {
  TiXmlDocument Doc;
  TiXmlElement* Resources = nullptr;
  TiXmlElement* Object = nullptr;
  if( TinyXML::LoadDocument( m_XrcFileName, &Doc ) ) {
    Resources = Doc.FirstChildElement( "resource" );
  }
  if( !Resources ) {
    Doc.Clear();
    Doc.InsertEndChild( TiXmlDeclaration( "utf-8", "" ) );
    Resources = Doc.InsertEndChild( TiXmlElement( "resource" ) )->ToElement();
  }
  for( Object = Resources->FirstChildElement( "object" ); Object; Object = Object->NextSiblingElement( "object" ) ) {
    if( cbC2U( Object->Attribute( "name" ) ) == m_ClassName ) {
      Object->Clear();
      while( Object->FirstAttribute() ) {
        Object->RemoveAttribute( Object->FirstAttribute()->Name() );
      }
      break;
    }
  }
  if( !Object ) {
    Object = Resources->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
  }
  m_RootItem->XmlWrite( Object, true, false );
  Object->SetAttribute( "name", cbU2C( m_ClassName ) );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    TiXmlElement* ToolElement = Object->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
    m_Tools[i]->XmlWrite( ToolElement, true, false );
  }
  return TinyXML::SaveDocument( m_XrcFileName, &Doc );
}

void wxsItemResData::BeginChange() {
  if( !m_LockCount++ ) {
    StoreTreeExpandState();
    wxsResourceTree::Get()->BlockSelect();
  }
}

void wxsItemResData::EndChange() {
  if( !--m_LockCount ) {
    m_Corrector.GlobalCheck();
    StoreUndo();
    if( M_Code_Editor ) {
      M_Code_Editor->UpdateModified();
    }
    if( M_Code_Editor ) {
      M_Code_Editor->RebuildPreview();
    }
    if( ValidateRootSelection() ) {
      m_RootSelection->NotifyPropertyChange( false );
    } else {
      m_RootSelection->ShowInPropertyGrid();
      if( M_Code_Editor ) {
        M_Code_Editor->RebuildQuickProps( m_RootSelection );
      }
    }
    RebuildFiles();
    RebuildTree();
    wxsResourceTree::Get()->UnblockSelect();
  }
}

bool wxsItemResData::ValidateRootSelection() {
  wxsItem* NewSelection = 0;
  if( ValidateRootSelectionReq( m_RootItem, NewSelection ) ) {
    return true;
  }
  for( int i = 0; i < GetToolsCount(); i++ ) {
    if( ValidateRootSelectionReq( m_Tools[i], NewSelection ) ) {
      return true;
    }
  }
  m_RootSelection = NewSelection ? NewSelection : m_RootItem;
  return false;
}

bool wxsItemResData::ValidateRootSelectionReq( wxsItem* Item, wxsItem*& NewSelection ) {
  if( Item == m_RootSelection ) {
    return true;
  }
  if( Item->GetIsSelected() && !NewSelection ) {
    NewSelection = Item;
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      if( ValidateRootSelectionReq( AsParent->GetChild( i ), NewSelection ) ) {
        return true;
      }
    }
  }
  return false;
}

bool wxsItemResData::CanPaste() {
  if( !m_RootItem->ConvertToParent() ) {
    return false;
  }
  if( !wxTheClipboard->Open() ) {
    return false;
  }
  bool Res = wxTheClipboard->IsSupported( wxsDF_WIDGET );
  wxTheClipboard->Close();
  return Res;
}

void wxsItemResData::Cut() {
  Copy();
  BeginChange();
  DeleteSelected();
  EndChange();
}

void wxsItemResData::Copy() {
  if( !wxTheClipboard->Open() ) {
    return;
  }
  wxsItemResDataObject* Data = new wxsItemResDataObject;
  CopyReq( m_RootItem, Data );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    CopyReq( m_Tools[i], Data );
  }
  wxTheClipboard->SetData( Data );
  wxTheClipboard->Close();
}

void wxsItemResData::CopyReq( wxsItem* Item, wxsItemResDataObject* Data ) {
  if( Item->GetIsSelected() ) {
    Data->AddItem( Item );
    return;
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      CopyReq( AsParent->GetChild( i ), Data );
    }
  }
}

void wxsItemResData::Paste( wxsParent* Parent, int Position ) {
  if( !m_RootItem ) {
    return;
  }
  if( !wxTheClipboard->Open() ) {
    return;
  }
  wxsItemResDataObject Data;
  if( wxTheClipboard->GetData( Data ) ) {
    int Cnt = Data.GetItemCount();
    if( Cnt ) {
      BeginChange();
      m_RootItem->ClearSelection();
      m_RootSelection = 0;
      for( int i = 0; i < Cnt; i++ ) {
        wxsItem* Insert = Data.BuildItem( this, i );
        if( Insert ) {
          if( Insert->ConvertToTool() ) {
            if( InsertNewTool( Insert->ConvertToTool() ) ) {
              Insert->SetIsSelected( true );
              if( !m_RootSelection )
              { m_RootSelection = Insert; }
            }
          } else {
            if( InsertNew( Insert, Parent, Position++ ) ) {
              Insert->SetIsSelected( true );
              if( !m_RootSelection )
              { m_RootSelection = Insert; }
            }
          }
        }
      }
      if( !m_RootSelection ) {
        m_RootSelection = m_RootItem;
      }
      EndChange();
    }
  }
  wxTheClipboard->Close();
}

bool wxsItemResData::AnySelected() {
  if( AnySelectedReq( m_RootItem ) ) {
    return true;
  }
  for( int i = 0; i < GetToolsCount(); i++ ) {
    if( m_Tools[i]->GetIsSelected() ) {
      return true;
    }
  }
  return false;
}

bool wxsItemResData::AnySelectedReq( wxsItem* Item ) {
  if( Item->GetIsSelected() ) {
    return true;
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      if( AnySelectedReq( AsParent->GetChild( i ) ) ) {
        return true;
      }
    }
  }
  return false;
}

void wxsItemResData::StoreTreeExpandState() {
  StoreTreeExpandStateReq( m_RootItem );
  if( GetToolsCount() && m_ToolsId.IsOk() ) {
    m_ToolsNodeIsExpanded = wxsResourceTree::Get()->IsExpanded( m_ToolsId );
  }
  for( int i = 0; i < GetToolsCount(); i++ ) {
    StoreTreeExpandStateReq( m_Tools[i] );
  }
}

void wxsItemResData::StoreTreeExpandStateReq( wxsItem* Item ) {
  if( m_IdMap.find( Item ) != m_IdMap.end() ) {
    wxTreeItemId Id = m_IdMap[Item];
    if( Id.IsOk() ) {
      Item->SetIsExpanded( wxsResourceTree::Get()->IsExpanded( Id ) );
    }
  }
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      StoreTreeExpandStateReq( AsParent->GetChild( i ) );
    }
  }
}

void wxsItemResData::RestoreTreeExpandAndSelectionState() {
  RestoreTreeExpandAndSelectionStateReq( m_RootItem );
  if( GetToolsCount() && m_ToolsId.IsOk() ) {
    if( m_ToolsNodeIsExpanded ) {
      wxsResourceTree::Get()->Expand( m_ToolsId );
    } else
    { wxsResourceTree::Get()->Collapse( m_ToolsId ); }
  }
  for( int i = 0; i < GetToolsCount(); i++ ) {
    RestoreTreeExpandAndSelectionStateReq( m_Tools[i] );
  }
  wxsResourceItemId Id;
  if( FindId( Id, m_RootSelection ) ) {
    wxsResourceTree::Get()->SelectItem( Id, true );
  }
}

void wxsItemResData::RestoreTreeExpandAndSelectionStateReq( wxsItem* Item ) {
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      RestoreTreeExpandAndSelectionStateReq( AsParent->GetChild( i ) );
    }
  }
  if( m_IdMap.find( Item ) != m_IdMap.end() ) {
    wxTreeItemId Id = m_IdMap[Item];
    if( Id.IsOk() ) {
      if( Item->GetIsExpanded() ) {
        wxsResourceTree::Get()->Expand( Id );
      } else
      { wxsResourceTree::Get()->Collapse( Id ); }
    }
  }
}

bool wxsItemResData::SelectItem( wxsItem* Item, bool UnselectOther ) {
  if( UnselectOther ) {
    if( m_RootItem ) {
      m_RootItem->ClearSelection();
    }
    for( int i = 0; i < GetToolsCount(); i++ ) {
      m_Tools[i]->ClearSelection();
    }
  }
  if( !Item ) {
    Item = m_RootItem;
  }
  if( Item ) {
    Item->SetIsSelected( true );
    Item->ShowInPropertyGrid();
  }
  m_RootSelection = Item;
  if( M_Code_Editor ) {
    M_Code_Editor->RebuildQuickProps( Item );
    M_Code_Editor->UpdateSelection();
  }
  wxsResourceItemId Id;
  if( FindId( Id, Item ) ) {
    if( wxsResourceTree::Get()->GetSelection() != Id ) {
      wxsResourceTree::Get()->SelectItem( Id, true );
    }
  }
  bool Changed = false;
  wxsItem* Child = Item;
  for( wxsParent* Parent = Item->GetParent(); Parent; Child = Parent, Parent = Parent->GetParent() ) {
    if( Parent->EnsureChildPreviewVisible( Child ) ) {
      Changed = true;
    }
  }
  if( Changed && M_Code_Editor ) {
    M_Code_Editor->RebuildPreview();
  }
  return true;
}

void wxsItemResData::NotifyChange( wxsItem* Changed ) {
  m_Corrector.AfterChange( Changed );
  Changed->NotifyPropertyChange( false );
  StoreUndo();
  RebuildFiles();
  if( M_Code_Editor ) {
    M_Code_Editor->UpdateModified();
    M_Code_Editor->RebuildPreview();
    M_Code_Editor->UpdateSelection();
  }
}

wxString wxsItemResData::GetXmlData() {
  wxsItemResDataObject Object;
  Object.AddItem( m_RootItem );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    Object.AddItem( m_Tools[i] );
  }
  return Object.GetXmlData();
}

bool wxsItemResData::SetXmlData( const wxString& XmlData ) {
  if( m_LockCount ) {
    return false;
  }
  wxsItemResDataObject Object;
  Object.SetXmlData( XmlData );
  wxsItem* NewRoot = Object.BuildItem( this, 0 );
  if( NewRoot->GetClassName() != m_ClassType ) {
    delete NewRoot;
    return false;
  }
  delete m_RootItem;
  m_RootItem = NewRoot;
  for( int i = 0; i < GetToolsCount(); i++ ) {
    delete m_Tools[i];
  }
  m_Tools.Clear();
  for( int i = 1; i < Object.GetItemCount(); i++ ) {
    wxsItem* NewItem = Object.BuildItem( this, i );
    if( !NewItem->ConvertToTool() ) {
      delete NewItem;
      continue;
    }
    wxsTool* Tool = NewItem->ConvertToTool();
    if( !Tool->CanAddToResource( this, false ) ) {
      delete Tool;
      continue;
    }
    InsertNewTool( Tool );
  }
  RebuildFiles();
  RebuildTree();
  SelectItem( m_RootItem, true );
  if( M_Code_Editor ) {
    M_Code_Editor->RebuildPreview();
    M_Code_Editor->UpdateModified();
  }
  return true;
}

bool wxsItemResData::InsertNew( wxsItem* New, wxsParent* Parent, int Position ) {
  if( !New ) {
    return false;
  }
  if( New->ConvertToTool() ) {
    return InsertNewTool( New->ConvertToTool() );
  }
  m_Corrector.BeforePaste( New );
  if( !Parent || !Parent->AddChild( New, Position ) ) {
    delete New;
    return false;
  }
  return true;
}

bool wxsItemResData::InsertNewTool( wxsTool* Tool ) {
  if( !Tool ) {
    return false;
  }
  if( !Tool->CanAddToResource( this, false ) ) {
    delete Tool;
    return false;
  }
  m_Corrector.BeforePaste( Tool );
  m_Tools.Add( Tool );
  return true;
}

void wxsItemResData::DeleteSelected() {
  DeleteSelectedReq( m_RootItem );
  for( int i = 0; i < GetToolsCount(); i++ ) {
    if( m_Tools[i]->GetIsSelected() ) {
      delete m_Tools[i];
      m_Tools.RemoveAt( i );
      i--;
    }
  }
  m_RootSelection = m_RootItem;
  m_RootSelection->SetIsSelected( true );
}

void wxsItemResData::DeleteSelectedReq( wxsItem* Item ) {
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      wxsItem* Child = AsParent->GetChild( i );
      if( Child->GetIsSelected() ) {
        AsParent->UnbindChild( i );
        delete Child;
        i--;
      } else
      { DeleteSelectedReq( Child ); }
    }
  }
}

bool wxsItemResData::ShowPreview() {
  if( m_Preview ) {
    return false;
  }
  m_Preview = BuildExactPreview( M_Code_Editor );
  return m_Preview != 0;
}

bool wxsItemResData::HidePreview() {
  if( !m_Preview ) {
    return false;
  }
  m_Preview->Destroy();
  m_Preview = 0;
  return true;
}

void wxsItemResData::RebuildTree() {
  if( !M_Code_Editor ) {
    return;
  }
  wxsResourceTree::Get()->DeleteChildren( m_TreeId );
  m_RootItem->BuildItemTree( wxsResourceTree::Get(), m_TreeId, -1 );
  if( GetToolsCount() ) {
    m_ToolsId = wxsResourceTree::Get()->AppendItem( m_TreeId, _T( "Tools" ), ToolsTreeImageId, ToolsTreeImageId );
    for( int i = 0; i < GetToolsCount(); i++ ) {
      m_Tools[i]->BuildItemTree( wxsResourceTree::Get(), m_ToolsId, -1 );
    }
  }
  StoreTreeIds();
  RestoreTreeExpandAndSelectionState();
}

void wxsItemResData::StoreTreeIds() {
  m_IdMap.clear();
  if( m_RootItem ) {
    StoreTreeIdsReq( m_RootItem );
  }
  for( int i = 0; i < GetToolsCount(); i++ ) {
    StoreTreeIdsReq( m_Tools[i] );
  }
}

void wxsItemResData::StoreTreeIdsReq( wxsItem* Item ) {
  m_IdMap[Item] = Item->GetLastTreeItemId();
  wxsParent* AsParent = Item->ConvertToParent();
  if( AsParent ) {
    for( int i = 0; i < AsParent->GetChildCount(); i++ ) {
      StoreTreeIdsReq( AsParent->GetChild( i ) );
    }
  }
}

bool wxsItemResData::FindId( wxsResourceItemId& Id, wxsItem* Item ) {
  ItemToIdMapT::iterator it =  m_IdMap.find( Item );
  if( it == m_IdMap.end() ) {
    return false;
  }
  Id = it->second;
  return true;
}
